package xyz.longicorn.driver.shiro;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import xyz.longicorn.driver.dao.LoginUserDao;
import xyz.longicorn.driver.pojo.LoginUser;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
public class AuthFilter extends BasicHttpAuthenticationFilter {

    private LoginUserDao loginUserDao;

    // 构造把需要的资源 由其他对象传输过来
    public AuthFilter(LoginUserDao loginUserDao){
        this.loginUserDao = loginUserDao;
    }


    private static final String AUTH_TOKEN_HEADER = "Authorization";

    //判断是否允许通过
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        log.debug("判断是否允许通过");
        try {
            // 执行登录 也可以重写此方法 自行实现登录
            return executeLogin(request, response);
        } catch (Exception e) {
            log.error("执行允许通过异常:" + e.getMessage(), e);
            //responseError(response, "auth check fail");
            return false;
        }
    }


    /**
     * 是否需要进行登录请求(false将不会继续后续操纵)
     * 简单的判断是否存在对应的凭证
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        return ((HttpServletRequest) request).getHeader(AUTH_TOKEN_HEADER) != null;
    }

    /**
     * 读取请求的token头信息
     * 创建shiro token
     *
     * @param request
     * @param response
     * @return
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String token = req.getHeader(AUTHORIZATION_HEADER); //
        if (StringUtils.hasLength(token)) {
            return new SimpleHeaderToken(token);
        }
        return null;
    }

    /**
     * isAccessAllowed为false时调用，验证失败
     *
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        this.sendChallenge(request, response);
        responseError(response, "login token check failure!");
        return false;
    }

    /**
     * 验证成功调用 可以将一些信息放入内存,以及更新token有效期
     *
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                     ServletRequest request, ServletResponse response) throws Exception {
        // 获取token
        String tokenValue = (String)token.getPrincipal();
        // 通过token 获取登录用户信息
        final LoginUser loginUser = loginUserDao.get(tokenValue);
        if(null != loginUser){
            // 更新token有效期
            loginUserDao.refreshToken(tokenValue);
            return true;
        }
        return false;

    }

    // 前置判断 - 过滤的主要流程
    // 过滤的入口 如果返回false 表示不需要过滤 不进行后续代码执行
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        // 针对 options 直接通过
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            ((HttpServletResponse) response).setStatus(HttpStatus.OK.value());
            setCors(response); // 直接允许跨域
            return false;
        }
        //判断用户是否想要登入 -- 判断header中有没有 token
        if (!isLoginAttempt(request, response)) {
            responseError(response, "verify token failure(没有token)");
            return false;
        }
        // 如果只是简单判断 可以不用调用父级的preHandle 当然也就不需要重写其他的方法
        return super.preHandle(request, response);
    }

    private void setCors(ServletResponse response) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.addHeader("Access-Control-Allow-Methods", "*");
        httpServletResponse.addHeader("Access-Control-Allow-Headers", "*");
    }

    private void responseError(ServletResponse response, Object msg) {
        setCors(response);
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(401);
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json;charset=UTF-8");
        try {
            String rj = msg instanceof String ? "{\"code\":401,\"message\":\"" + (String) msg + "\"}" : new ObjectMapper().writeValueAsString(msg);
            httpResponse.getWriter().append(rj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
